package fr.lip6.meta.ple.featureIdentification;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


import fr.lip6.meta.tools.Trigger;

/**
 * Transformer based on feature identification
 * @author Luz
 */
public class PlFeatureReduceLts {
	
	/**
	 * Design Pattern Strategy - choose a common set of triggers
	 */
	private FeaturePriorityStrategy priority;
	
	/**
	 * Root - has a valid value after call transform() with a valid lts
	 */
	private FeatureNode root = null;
	
	/**
	 * Products - has a valid value after call transform() with a valid lts
	 */
	private List<Product> products = null;

	/**
	 * Constructor
	 * @param p - priority strategy
	 */
	public PlFeatureReduceLts (FeaturePriorityStrategy p) {
		priority = p;
	}
	
	/**
	 * Constructor with priority par default
	 */
	public PlFeatureReduceLts () {
		this(new MoreFrequentTrigger());
	}
		
	
	/**
	 * Prints in a file the features information
	 * @param fileName - the name of the destination file
	 * @throws NullRootException
	 * @throws IOException
	 */
	public Collection<Feature> featuresToFile(String fileName) throws NullRootException,
			IOException {
		if (root == null) throw new NullRootException();
        FileWriter fw = new FileWriter(fileName);;
        PrintWriter pw = new PrintWriter(fw);
        int pSize = products.size();
        
        //Extracting information
        ArrayList<Feature> features = extractFeatures(root);
		mergeAllIfEquivalents(features);
		canonizeFeatures(features);
		mergeAllIfEquivalents(features);
		identifyFeatures(features);

		//Printing information
		pw.println("PRODUCTS - FEATURES:");
        for (Product prod : products)
        	pw.println(prod.getId() + ": " +
        			prod.featuresImplementedString(features));
        pw.println("\n\nPRODUCTS: " + products.size() + "\n");
        for (Product prod : products)
        	pw.println(prod);
        pw.println("\n\nFEATURES: " + features.size() + "\n");
        for (Feature f : features) {
        	Collection<Integer> p = f.getProdIds();
        	int rate = (p.size() * 100) / pSize;
        	pw.println(f.getId() + " [rate " + rate + "%]: " + p);
        	pw.println(f.toStringLines(1));
        }
        fw.close();
        return features;
	}
	
	/**
	 * Searches for a common set of triggers and do the merging with their
	 * children
	 * @param node - the node
	 */
	@SuppressWarnings("unused")
	private void mergeChildren(FeatureNode node) {
		Collection<Trigger> common = priority.chooseFeatureToMerge(node.getChildren());
		while (!common.isEmpty()) {
			node.expand(common);
			common = priority.chooseFeatureToMerge(node.getChildren());
		}
		for (FeatureNode child : node.getChildren()) {
			mergeChildren(child);
		}
	}
	
	/**
	 * Extracts the features linked to a node
	 * @param node - the FeatureNode
	 * @return the list of founded features
	 */
	private ArrayList<Feature> extractFeatures(FeatureNode node) {
		ArrayList<Feature> features = new ArrayList<Feature>();
		features.add(Feature.featureFromNode(node));
		for (FeatureNode child : node.getChildren())
			features.addAll(extractFeatures(child));
		return features;
	}
	
	/**
	 * Merges (deletes the repeated features without product information loss) a
	 * list of features 
	 * @param features - the list of features
	 */
	private void mergeAllIfEquivalents(List<Feature> features) {
		ArrayList<Feature> forRemove = new ArrayList<Feature>();
		for (int i = 0; i < features.size() - 1; i++) {
			forRemove.clear();
			Feature fi = features.get(i);
			for (int j = i + 1; j < features.size(); j++) {
				Feature fj = features.get(j);
				if (fi.mergeIfEquivalents(fj))
					forRemove.add(fj);
			}
			features.removeAll(forRemove);
		}
	}
	
	/**
	 * Canonizes a list of features.
	 * Canonization: transformation of two features (F1 U F2) and F2 in F1 and
	 * F2
	 * @param features - the list of features
	 */
	private void canonizeFeatures(List<Feature> features) {
		ArrayList<Feature> forRemove = new ArrayList<Feature>();
		for (int i = 0; i < features.size() - 1; i++) {
			forRemove.clear();
			Feature fi = features.get(i);
			for (int j = i + 1; j < features.size(); j++) {
				Feature fj = features.get(j);
				Feature norm = fj.canonize(fi);
				if (norm != null) {
					features.add(norm);
					if (fj.isEmpty()) forRemove.add(fj);
					if (fi.isEmpty()) {
						forRemove.add(fi);
						i--;
					}
				}
			}
			features.removeAll(forRemove);
		}
	}
	
	/**
	 * Sets an id for each feature
	 * @param features
	 */
	private void identifyFeatures(List<Feature> features) {
		int id = 0;
		for (Feature f : features) {
			f.setId("F" + id++);
		}
	}
}
